package com.specificgroup.jira.plugin.acpro.action;

import com.atlassian.plugin.webresource.WebResourceManager;
import com.specificgroup.jira.plugin.acpro.Constants;
import com.specificgroup.jira.plugin.acpro.exception.AcproException;
import com.specificgroup.jira.plugin.acpro.mapping.AcproCategory;
import com.specificgroup.jira.plugin.acpro.mapping.AcproProductVersion;
import com.specificgroup.jira.plugin.acpro.mapping.AcproStatus;
import com.specificgroup.jira.plugin.acpro.mapping.AcproUrgency;
import com.specificgroup.jira.plugin.acpro.mapping.IssueTypeMappingData;
import com.specificgroup.jira.plugin.acpro.mapping.JiraIssueType;
import com.specificgroup.jira.plugin.acpro.mapping.JiraPriority;
import com.specificgroup.jira.plugin.acpro.mapping.JiraProjectVersion;
import com.specificgroup.jira.plugin.acpro.mapping.JiraStatus;
import com.specificgroup.jira.plugin.acpro.mapping.PriorityMappingData;
import com.specificgroup.jira.plugin.acpro.mapping.ProjectVersionMappingData;
import com.specificgroup.jira.plugin.acpro.mapping.StatusMappingData;
import com.specificgroup.jira.plugin.acpro.model.Settings;
import com.specificgroup.jira.plugin.acpro.service.AcproManager;
import com.specificgroup.jira.plugin.acpro.service.JiraAcproPluginJiraManager;
import com.specificgroup.jira.plugin.acpro.session.SessionTempData;
import com.specificgroup.jira.plugin.acpro.session.SessionUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author Ivan Holub
 */
public class IssueMappingAction extends AcproActionSupport {

    private static final String NEXT_ACTION = "AcproReview!default.jspa";

    private static final String RESOURCE_KEY = "com.specificgroup.jira.plugin.acpro.jira-acpro-plugin:acpro-resource-js-issue-mapping";
    
    private static final String ISSUE_TYPE_PARAMETER_NAME = "issueTypeMapping";

    private static final String PRIORITY_PARAMETER_NAME = "priorityMapping";

    private static final String STATUS_PARAMETER_NAME = "statusMapping";

    private static final String PROJECT_VERSION_PARAMETER_NAME = "projectVersionMapping";
    
    private Logger log = Logger.getLogger(getClass());

    private AcproManager acproManager;

    private WebResourceManager webResourceManager;

    private IssueTypeMappingData issueTypeMappingData;

    private PriorityMappingData priorityMappingData;

    private Map<String, String> issueTypeMappingStr;

    private Map<String, String> priorityMappingStr;

    private Map<String, String> statusMappingStr;

    private StatusMappingData statusMappingData;

    private String defaultJiraIssueTypeId;

    private Map<String, Long> projectVersionMappingStr;

    private ProjectVersionMappingData projectVersionMappingData;

    private Long jiraProjectId;

    private JiraAcproPluginJiraManager jiraAcproPluginJiraManager;

    public Long getJiraProjectId() {
        return jiraProjectId;
    }

    public ProjectVersionMappingData getProjectVersionMappingData() {
        return projectVersionMappingData;
    }

    public void setDefaultJiraIssueTypeId(String defaultJiraIssueTypeId) {
        this.defaultJiraIssueTypeId = defaultJiraIssueTypeId;
    }

    public StatusMappingData getStatusMappingData() {
        return statusMappingData;
    }

    public PriorityMappingData getPriorityMappingData() {
        return priorityMappingData;
    }

    public IssueTypeMappingData getIssueTypeMappingData() {
        return issueTypeMappingData;
    }

    public IssueMappingAction(AcproManager acproManager, JiraAcproPluginJiraManager jiraAcproPluginJiraManager, WebResourceManager webResourceManager) {
        this.acproManager = acproManager;
        this.jiraAcproPluginJiraManager = jiraAcproPluginJiraManager;
        this.webResourceManager = webResourceManager;
    }

    @Override
    public String doDefault() throws Exception {
        if (!hasPermissions()) {
            return PERMISSION_VIOLATION_RESULT;
        }

        webResourceManager.requireResource(RESOURCE_KEY);
        webResourceManager.requireResource(Constants.JQUERY_RESOURCE_KEY);

        Settings settings = SessionUtil.getSettings();

        jiraProjectId = settings.getJiraProjectId();

        try {
            issueTypeMappingData = acproManager.prepareIssueTypeMapping(settings);
            priorityMappingData = acproManager.preparePriorityMapping(settings);
            statusMappingData = acproManager.prepareStatusMappingData(settings);
            projectVersionMappingData = acproManager.prepareProjectVersionMapping(settings);
        } catch (IOException e) {
            log.error("Error", e);
            addErrorMessage(e.toString());
            return ERROR;
        } catch (AcproException e) {
            log.error("Error", e);
            addErrorMessage(e.getMessage());
            return ERROR;
        }

        SessionTempData sessionTempData = SessionUtil.getSessionTempData();
        sessionTempData.setIssueTypeMappingData(issueTypeMappingData);
        sessionTempData.setPriorityMappingData(priorityMappingData);
        sessionTempData.setStatusMappingData(statusMappingData);
        sessionTempData.setProjectVersionMappingData(projectVersionMappingData);

        return super.doDefault();
    }

    @Override
    protected void doValidation() {
        boolean hasErrors = false;
        //from session
        IssueTypeMappingData issueTypeMappingData = SessionUtil.getSessionTempData().getIssueTypeMappingData();
        Map<String, JiraIssueType> jiraIssueTypes = jiraAcproPluginJiraManager.getJiraIssueTypesMap(); //jira issue types
        issueTypeMappingStr = new HashMap<String, String>(); //acpro category key -> jira issue type id
        for (AcproCategory acproCategory: issueTypeMappingData.getAcproCategories()) {
            String key = acproCategory.getKey();
            String jiraId = request.getParameter(ISSUE_TYPE_PARAMETER_NAME + key);
            if (!isJiraIssueTypeIdValid(jiraIssueTypes, jiraId)) {
                hasErrors = true;
                addErrorMessage(getText("error.issueTypeMapping", acproCategory.getName()));
            } else {
                issueTypeMappingStr.put(key, jiraId);
            }
        }

        if (issueTypeMappingData.isItemsWithoutCategoryExist()) {
            if (!isJiraIssueTypeIdValid(jiraIssueTypes, defaultJiraIssueTypeId)) {
                hasErrors = true;
                addErrorMessage(getText("error.categoryIsNotSet"));
            }
        }

        //from session
        PriorityMappingData priorityMappingData = SessionUtil.getSessionTempData().getPriorityMappingData();
        Map<String, JiraPriority> jiraPriorities = jiraAcproPluginJiraManager.getJiraPrioritiesMap(); //jira Priority
        priorityMappingStr = new HashMap<String, String>(); //acpro urgency key -> jira priority id
        for (AcproUrgency acproUrgency: priorityMappingData.getAcproUrgencies()) {
            String key = acproUrgency.getKey();
            String jiraId = request.getParameter(PRIORITY_PARAMETER_NAME + key);
            if (!isJiraPriorityIdValid(jiraPriorities, jiraId)) {
                hasErrors = true;
                addErrorMessage(getText("error.priorityMapping", acproUrgency.getName()));
            } else {
                priorityMappingStr.put(key, jiraId);
            }
        }

        //from session
        StatusMappingData statusMappingData = SessionUtil.getSessionTempData().getStatusMappingData();
        Map<String, JiraStatus> jiraStatuses = jiraAcproPluginJiraManager.getJiraStatusesMap(); //jira Status
        statusMappingStr = new HashMap<String, String>(); //acpro Status key -> jira Status id
        for (AcproStatus acproStatus: statusMappingData.getAcproStatuses()) {
            String key = acproStatus.getKey();
            String jiraId = request.getParameter(STATUS_PARAMETER_NAME + key);
            if (!isJiraStatusIdValid(jiraStatuses, jiraId)) {
                hasErrors = true;
                addErrorMessage(getText("error.statusMapping", acproStatus.getName()));
            } else {
                statusMappingStr.put(key, jiraId);
            }
        }

        //from session
        ProjectVersionMappingData projectVersionMappingData = SessionUtil.getSessionTempData().getProjectVersionMappingData();
        Settings settings = SessionUtil.getSettings();
        Long projectId = settings.getJiraProjectId();
        Map<Long, JiraProjectVersion> jiraProjectVersionsMap = jiraAcproPluginJiraManager.getJiraProjectVersionsMap(projectId); //jira Project Versions
        projectVersionMappingStr = new HashMap<String, Long>(); //acpro index -> jira Project Version id
        for (AcproProductVersion acproProductVersion: projectVersionMappingData.getAcproProductVersions()) {
            String acproId = acproProductVersion.getKey();
            String jiraIdStr = request.getParameter(PROJECT_VERSION_PARAMETER_NAME + acproId);
            boolean isSet = false;
            if (StringUtils.isNotEmpty(jiraIdStr)) {
                Long jiraId = Long.valueOf(jiraIdStr);
                if (isJiraProjectVersionIdValid(jiraProjectVersionsMap, jiraId)) {
                    isSet = true;
                    projectVersionMappingStr.put(acproId, jiraId);
                }
            }
            if (!isSet) {
                hasErrors = true;
                addErrorMessage(getText("error.projectVersionMapping", acproProductVersion.getName()));
            }
        }

        if (hasErrors) {
            webResourceManager.requireResource(RESOURCE_KEY);
            webResourceManager.requireResource(Constants.JQUERY_RESOURCE_KEY);

            jiraProjectId = settings.getJiraProjectId();

            Map<String, JiraIssueType> issuesMapping = issueTypeMappingData.getIssueTypesMapping();
            issuesMapping.clear();
            for (String acproKey: issueTypeMappingStr.keySet()) {
                String jiraId = issueTypeMappingStr.get(acproKey); //jira issue type id
                JiraIssueType jiraObj = jiraIssueTypes.get(jiraId);
                issuesMapping.put(acproKey, jiraObj);
            }
            if (issueTypeMappingData.isItemsWithoutCategoryExist()) {
                JiraIssueType jiraObj = jiraIssueTypes.get(defaultJiraIssueTypeId);
                issueTypeMappingData.setDefaultJiraIssueTypeId(defaultJiraIssueTypeId);
                issueTypeMappingData.setDefaultJiraIssueType(jiraObj);
            }

            Map<String, JiraPriority> prioritiesMapping = priorityMappingData.getPrioritiesMapping();
            prioritiesMapping.clear();
            for (String acproKey: priorityMappingStr.keySet()) {
                String jiraId = priorityMappingStr.get(acproKey); //jira priority id
                JiraPriority jiraObj = jiraPriorities.get(jiraId);
                prioritiesMapping.put(acproKey, jiraObj);
            }
            
            Map<String, JiraStatus> statusesMapping = statusMappingData.getStatusesMapping();
            statusesMapping.clear();
            for (String acproKey: statusMappingStr.keySet()) {
                String jiraId = statusMappingStr.get(acproKey); //jira Status id
                JiraStatus jiraObj = jiraStatuses.get(jiraId);
                statusesMapping.put(acproKey, jiraObj);
            }

            Map<String, JiraProjectVersion> projectVersionMapping = projectVersionMappingData.getProjectVersionMapping();
            projectVersionMapping.clear();
            for (String acproId: projectVersionMappingStr.keySet()) {
                Long jiraId = projectVersionMappingStr.get(acproId); //jira Project Version id
                JiraProjectVersion jiraObj = jiraProjectVersionsMap.get(jiraId);
                projectVersionMapping.put(acproId, jiraObj);
            }

            //show form again
            this.issueTypeMappingData = issueTypeMappingData;
            this.priorityMappingData = priorityMappingData;
            this.statusMappingData = statusMappingData;
            this.projectVersionMappingData = projectVersionMappingData;
        }

        super.doValidation();
    }

    private boolean isJiraIssueTypeIdValid(Map<String, JiraIssueType> jiraIssueTypes, String issueTypeId) {
        return StringUtils.isNotEmpty(issueTypeId) && jiraIssueTypes.containsKey(issueTypeId);
    }

    private boolean isJiraPriorityIdValid(Map<String, JiraPriority> jiraPriorities, String priorityId) {
        return StringUtils.isNotEmpty(priorityId) && jiraPriorities.containsKey(priorityId);
    }

    private boolean isJiraStatusIdValid(Map<String, JiraStatus> jiraStatuses, String statusId) {
        return StringUtils.isNotEmpty(statusId) && jiraStatuses.containsKey(statusId);
    }

    private boolean isJiraProjectVersionIdValid(Map<Long, JiraProjectVersion> jiraProjectVersions, Long projectVersionId) {
        return jiraProjectVersions.containsKey(projectVersionId);
    }

    @Override
    protected String doExecute() throws Exception {
        if (!hasPermissions()) {
            return PERMISSION_VIOLATION_RESULT;
        }
        
        Settings settings = SessionUtil.getSettings();
        settings.setIssueTypeMapping(issueTypeMappingStr);
        settings.setPriorityMapping(priorityMappingStr);
        settings.setStatusMapping(statusMappingStr);
        IssueTypeMappingData issueTypeMappingData = SessionUtil.getSessionTempData().getIssueTypeMappingData();
        if (issueTypeMappingData.isItemsWithoutCategoryExist()) {
            settings.setCategoryIsNotSetJiraIssueTypeIdMapping(defaultJiraIssueTypeId);
        }
        Map<String, Long> projectVersionMapping = new HashMap<String, Long>();
        List<AcproProductVersion> acproProductVersions = SessionUtil.getSessionTempData()
                .getProjectVersionMappingData().getAcproProductVersions();
        for (String acproId: projectVersionMappingStr.keySet()) {
            Long jiraId = projectVersionMappingStr.get(acproId);
            AcproProductVersion apv = findAcproProductVersion(acproProductVersions, acproId);
            projectVersionMapping.put(apv.getName(), jiraId);
        }
        settings.setProjectVersionMapping(projectVersionMapping);
        String redirect = getRedirect(NEXT_ACTION);
        return redirect;
    }

    private AcproProductVersion findAcproProductVersion(List<AcproProductVersion> acproProductVersions, String id) {
        for (AcproProductVersion apv: acproProductVersions) {
            if (id.equals(apv.getKey())) {
                return apv;
            }
        }
        throw new RuntimeException("AcproProductVersion is not found"); //must not happen
    }
}
